/*
 * Copyright @ 2017 - Present, 8x8 Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jitsi.videobridge;

import org.jetbrains.annotations.*;
import org.jitsi.utils.logging2.*;
import org.jitsi.videobridge.message.*;
import org.jitsi.videobridge.util.*;
import org.json.simple.*;

import java.io.*;


/**
 * Handles the functionality related to sending and receiving COLIBRI messages
 * for an {@link Endpoint}. An abstract implementation.
 *
 * @author Boris Grozev
 */
public abstract class AbstractEndpointMessageTransport<T extends AbstractEndpoint>
    extends MessageHandler
{
    /**
     * The {@link Logger} to be used by this instance to print debug
     * information.
     */
    protected final @NotNull Logger logger;

    /**
     * Initializes a new {@link AbstractEndpointMessageTransport} instance.
     */
    public AbstractEndpointMessageTransport(@NotNull Logger parentLogger)
    {
        this.logger = parentLogger.createChildLogger(getClass().getName());
    }

    /**
     *
     * @return true if this message transport is 'connected', false otherwise
     */
    public abstract boolean isConnected();

    /**
     * Fires the message transport ready event for the associated endpoint.
     */
    protected void notifyTransportChannelConnected()
    {
    }

    /**
     * Notifies this {@link EndpointMessageTransport} that a specific message
     * has been received on a specific transport channel.
     * @param src the transport channel on which the message has been received.
     * @param msg the message that was received.
     */
    public void onMessage(Object src, String msg)
    {
        BridgeChannelMessage message;

        try
        {
            message = BridgeChannelMessage.parse(msg);
        }
        catch (IOException ioe)
        {
            logger.warn("Invalid message received: " + msg, ioe);
            return;
        }

        TaskPools.IO_POOL.submit(() ->
        {
            BridgeChannelMessage response = handleMessage(message);
            if (response != null)
            {
                sendMessage(src, response);
            }
        });
    }

    /**
     * Sends a specific message over the active transport channel of this
     * {@link EndpointMessageTransport}.
     *
     * @param msg message text to send.
     */
    protected void sendMessage(BridgeChannelMessage msg)
    {
    }

    protected void sendMessage(Object dst, BridgeChannelMessage message)
    {
    }

    /**
     * Closes this {@link EndpointMessageTransport}.
     */
    protected void close()
    {
    }

    @SuppressWarnings("unchecked")
    public JSONObject getDebugState() {
        JSONObject receivedCounts = new JSONObject();
        getReceivedCounts().forEach(receivedCounts::put);

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("received_counts", receivedCounts);

        return jsonObject;
    }

    /**
     * Events generated by {@link AbstractEndpointMessageTransport} types which
     * are of interest to other entities.
     */
    interface EndpointMessageTransportEventHandler {
        void endpointMessageTransportConnected(@NotNull AbstractEndpoint endpoint);
    }
}
